﻿/*
 * ©2009-2011 XELF
 * http://xelf.info
 */

namespace XELF.Framework {

	/// <summary>
	/// 二元数
	/// </summary>
	public partial struct DualSingle {
		/// <summary>
		/// 実部
		/// </summary>
		public float Real;
		/// <summary>
		/// 非実部
		/// </summary>
		public float Dual;

		/// <summary>
		/// コンストラクタ
		/// </summary>
		/// <param name="real">実部</param>
		/// <param name="dual">非実部</param>
		public DualSingle(float real, float dual) {
			Real = real;
			Dual = dual;
		}

		public static DualSingle Add(DualSingle a, DualSingle b) {
			return new DualSingle(a.Real + b.Real, a.Dual + b.Dual);
		}
		public static void Add(ref DualSingle a, ref DualSingle b, out DualSingle result) {
			result = new DualSingle(a.Real + b.Real, a.Dual + b.Dual);
		}
		public static DualSingle Subtract(DualSingle a, DualSingle b) {
			return new DualSingle(a.Real - b.Real, a.Dual - b.Dual);
		}
		public static void Subtract(ref DualSingle a, ref DualSingle b, out DualSingle result) {
			result = new DualSingle(a.Real - b.Real, a.Dual - b.Dual);
		}
		public static DualSingle Negate(DualSingle a) {
			return new DualSingle(-a.Real, -a.Dual);
		}
		public void Negate() {
			Real = -Real;
			Dual = -Dual;
		}
		public static DualSingle Conjugate(DualSingle a) {
			return new DualSingle(a.Real, -a.Dual);
		}
		public void Conjugate() {
			Dual = -Dual;
		}
		/// <summary>
		/// 長さの2乗。this * Conjugate(this)。非実部は0になる。
		/// </summary>
		/// <returns></returns>
		public float LengthSquared() {
			return Real * Real;
		}
		public float Length() {
			return MathHelper.Sqrt(Real * Real);
		}
		public static DualSingle Rcp(DualSingle a) {
			var r = MathHelper.Rcp(a.Real);
			return new DualSingle(r, -a.Dual * r * r);
		}
		public static DualSingle Multiply(DualSingle a, DualSingle b) {
			var r = MathHelper.Rcp(b.Real);
			return new DualSingle(a.Real * b.Real, b.Real * a.Dual + a.Real * b.Dual);
		}
		public static DualSingle Divide(DualSingle a, DualSingle b) {
			var r = MathHelper.Rcp(b.Real);
			return new DualSingle(a.Real * r, (b.Real * a.Dual - a.Real * b.Dual) * r * r);
		}
		public static DualSingle Sqrt(DualSingle a) {
			var r = MathHelper.Rsqrt(a.Real);
			return new DualSingle(MathHelper.Rcp(r), 0.5f * a.Dual * r);
		}
		public static DualSingle Rsqrt(DualSingle a) {
			var r = MathHelper.Rsqrt(a.Real);
			return new DualSingle(r, -0.5f * a.Dual * r * r * r);
		}
		public static DualSingle Sin(DualSingle a) {
			return new DualSingle(MathHelper.Sin(a.Real), a.Dual * MathHelper.Cos(a.Real));
		}
		public static DualSingle Cos(DualSingle a) {
			return new DualSingle(MathHelper.Cos(a.Real), -a.Dual * MathHelper.Sin(a.Real));
		}
		public static DualSingle Asin(DualSingle a) {
			return new DualSingle(MathHelper.Asin(a.Real), a.Dual * MathHelper.Rsqrt(1 - a.Real * a.Real));
		}
		public static DualSingle Acos(DualSingle a) {
			if (a.Real >= 1) return new DualSingle(0, 0);
			if (a.Real <= -1) return new DualSingle(MathHelper.Pi, 0);
			return new DualSingle(MathHelper.Acos(a.Real), -a.Dual * MathHelper.Rsqrt(1 - a.Real * a.Real));
		}
		public static DualSingle Exp(DualSingle a) {
			var exp = MathHelper.Exp(a.Real);
			return new DualSingle(exp, a.Dual * exp);
		}
		public static DualSingle Log(DualSingle a) {
			return new DualSingle(MathHelper.Log(a.Real), a.Dual * MathHelper.Rcp(a.Real));
		}
		public static DualSingle Pow(DualSingle a, float b) {
			var p = MathHelper.Pow(a.Real, b - 1);
			return new DualSingle(p * a.Real, b * a.Dual * p);
		}

		public static bool AreEqual(ref DualSingle a, ref DualSingle b, float delta) {
			return
				MathHelper.AreEqual(a.Real, b.Real, delta) &&
				MathHelper.AreEqual(a.Dual, b.Dual, delta);
		}

	}

}
